{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Cookbook: Mind Map\n",
    "\n",
    "In this cookbook, we will demonstrate how to interact with files and create structured visual outputs like mind maps using an agent."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Install Requirements\n",
    "\n",
    "We will use [Qwen-Agent](https://github.com/QwenLM/Qwen-Agent) in this book. For demonstration, we will need at least the `mcp` functionality of Qwen-Agent."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip3 install -U \"qwen-agent[gui,rag,code_interpreter,mcp]\"\n",
    "# `pip install -U qwen-agent` will install the minimal requirements.\n",
    "# The optional requirements, specified in double brackets, are:\n",
    "#   [gui] for Gradio-based GUI support;\n",
    "#   [rag] for RAG support;\n",
    "#   [code_interpreter] for Code Interpreter support;\n",
    "#   [mcp] for MCP support."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip3 install -U uv\n",
    "# We will use mcp servers that run with the `uvx` command, so `uv` is required."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!npm --version\n",
    "# We also use mcp servers that run with the `npx` command, so node.js should be installed."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Create a Document\n",
    "\n",
    "Here, we create a sample document and save it as `example_document.md` for later use."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "document = '''\n",
    "# Document\n",
    "\n",
    "## Introduction\n",
    "- Brief overview of the document's purpose and main topics.\n",
    "\n",
    "## Background\n",
    "- Historical context and previous research.\n",
    "- Key theories and models discussed.\n",
    "\n",
    "## Methodology\n",
    "- Detailed description of the methods used in the study.\n",
    "- Explanation of data collection and analysis techniques.\n",
    "\n",
    "## Results\n",
    "- Presentation of the findings from the research.\n",
    "- Statistical analysis and graphical representations.\n",
    "\n",
    "## Discussion\n",
    "- Interpretation of the results.\n",
    "- Comparison with previous studies.\n",
    "- Limitations and future research directions.\n",
    "\n",
    "## Conclusion\n",
    "- Summary of the main points covered in the document.\n",
    "- Final thoughts and implications of the research.\n",
    "'''.strip()\n",
    "\n",
    "with open('example_document.md', 'w', encoding='utf-8') as f:\n",
    "    f.write(document)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Step 1: Create an Agent\n",
    "\n",
    "Create an agent that is capable of \n",
    "- listing, reading and writing files (through the MCP server `@modelcontextprotocol/server-filesystem`).\n",
    "- making mind maps (through the MCP server `mindmap-mcp-server`, source: [Mindmap MCP Server](https://github.com/YuChenSSR/mindmap-mcp-server))."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from qwen_agent.agents import Assistant\n",
    "from qwen_agent.utils.output_beautify import typewriter_print\n",
    "# `typewriter_print` prints streaming messages in a non-overlapping manner."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2025-05-01 21:24:32,290 - mcp_manager.py - 110 - INFO - Initializing MCP tools from mcp servers: ['filesystem', 'mindmap']\n",
      "2025-05-01 21:24:32,297 - mcp_manager.py - 245 - INFO - Initializing a MCP stdio_client, if this takes forever, please check the config of this mcp server: filesystem\n",
      "2025-05-01 21:24:33,332 - mcp_manager.py - 245 - INFO - Initializing a MCP stdio_client, if this takes forever, please check the config of this mcp server: mindmap\n"
     ]
    }
   ],
   "source": [
    "llm_cfg = {\n",
    "    'model': 'qwen3-32b',\n",
    "    'model_server': 'dashscope',\n",
    "    'api_key': '' # **fill your api key here**\n",
    "\n",
    "    # Use a model service compatible with the OpenAI API, such as vLLM or Ollama:\n",
    "    # 'model': 'Qwen3-8B',\n",
    "    # 'model_server': 'http://localhost:8000/v1',  # base_url, also known as api_base\n",
    "    # 'api_key': 'EMPTY'\n",
    "}\n",
    "\n",
    "tools = [{\n",
    "    \"mcpServers\": {\n",
    "        \"filesystem\": {\n",
    "            \"command\": \"npx\",\n",
    "            \"args\": [\n",
    "                \"-y\",\n",
    "                \"@modelcontextprotocol/server-filesystem\",\n",
    "                '.',\n",
    "            ]\n",
    "        },\n",
    "        \"mindmap\": {\n",
    "            \"command\": \"uvx\",\n",
    "            \"args\": [\"mindmap-mcp-server\", \"--return-type\", \"filePath\"]\n",
    "        }\n",
    "    }\n",
    "}]\n",
    "agent = Assistant(\n",
    "    llm=llm_cfg,\n",
    "    function_list=tools\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Step 2: Number the Headings\n",
    "\n",
    "We will add numbers to the Markdown headings to demonstrate how we can manipulate Markdown documents with the agent."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "messages = []\n",
    "messages += [{\"role\": \"user\", \"content\": \"Read the example_document.md file in the current folder. In your reply, show me the content with the second-level headings numbered. (example: ## heading -> ## 1. heading)\"}]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[THINK]\n",
      "Okay, the user wants me to read the example_document.md file in the current folder and then modify the content so that all second-level headings are numbered. Let me think about how to approach this.\n",
      "\n",
      "First, I need to read the file. The available tools include filesystem-read_file, which is suitable for reading a single file. So I'll start by calling that function with the path \"example_document.md\".\n",
      "\n",
      "Once I have the content, I need to process it. The task is to number the second-level headings. Second-level headings in Markdown start with \"##\". I'll need to find all lines that start with \"##\", then assign them a number in sequence. For example, the first occurrence becomes \"## 1. heading\", the next \"## 2. heading\", and so on.\n",
      "\n",
      "But wait, what if there are other lines that start with \"##\" but aren't headings? Like maybe in code blocks or quotes. But Markdown syntax usually uses \"##\" for headings, so maybe I can assume that any line starting with \"##\" followed by a space is a heading. I'll have to split the content into lines and check each line.\n",
      "\n",
      "So the plan is:\n",
      "1. Read the file content using filesystem-read_file.\n",
      "2. Split the content into lines.\n",
      "3. Iterate through each line, check if it's a second-level heading.\n",
      "4. Keep a counter, and replace the \"##\" with \"## X.\" where X is the counter.\n",
      "5. Join the lines back together and present the modified content to the user.\n",
      "\n",
      "Wait, but the user wants the reply to show the content with the headings numbered. So after processing, I just need to output the modified content in the response. However, the available functions don't include a way to modify the file unless I use filesystem-edit_file. But the user didn't ask to save the changes, just to show the content with numbered headings. So I should process the content in memory and then display it, not write it back to the file.\n",
      "\n",
      "Therefore, the steps are correct: read the file, process the content, and then reply with the modified content. Since the user is asking for the content to be shown, not saved, I don't need to use any write or edit functions. Just read and then output the processed content in the response.\n",
      "\n",
      "Now, let me make sure that the example_document.md exists in the current directory. The user mentioned \"current folder\", so the path is just the filename. Using filesystem-read_file with path \"example_document.md\" should work if it's in the allowed directories. Also, the current directory is likely allowed, but I need to confirm. Since the user didn't specify any other path, assuming it's allowed.\n",
      "\n",
      "So the first tool call is filesystem-read_file with path \"example_document.md\". Then, once I get the content, I can process it as described and present the result in the answer.\n",
      "[TOOL_CALL] filesystem-read_file\n",
      "{\"path\": \"example_document.md\"}\n",
      "[TOOL_RESPONSE] filesystem-read_file\n",
      "# Document\n",
      "\n",
      "## Introduction\n",
      "- Brief overview of the document's purpose and main topics.\n",
      "\n",
      "## Background\n",
      "- Historical context and previous research.\n",
      "- Key theories and models discussed.\n",
      "\n",
      "## Methodology\n",
      "- Detailed description of the methods used in the study.\n",
      "- Explanation of data collection and analysis techniques.\n",
      "\n",
      "## Results\n",
      "- Presentation of the findings from the research.\n",
      "- Statistical analysis and graphical representations.\n",
      "\n",
      "## Discussion\n",
      "- Interpretation of the results.\n",
      "- Comparison with previous studies.\n",
      "- Limitations and future research directions.\n",
      "\n",
      "## Conclusion\n",
      "- Summary of the main points covered in the document.\n",
      "- Final thoughts and implications of the research.\n",
      "[THINK]\n",
      "Okay, the user asked to read the example_document.md and show the content with second-level headings numbered. I first called the filesystem-read_file function to get the content. The response from the tool gave me the markdown content with several ## headings.\n",
      "\n",
      "Now, I need to process this content. The task is to number each second-level heading sequentially. Let me look at the structure. The original content has headings like \"## Introduction\", \"## Background\", etc. Each of these should be converted to \"## 1. Introduction\", \"## 2. Background\", and so on.\n",
      "\n",
      "I'll start by splitting the content into lines. Then, iterate through each line. Whenever I encounter a line that starts with \"## \", I'll increment a counter and replace the \"## \" with \"## X. \", where X is the current counter value. For other lines, I'll leave them as they are.\n",
      "\n",
      "Wait, I need to make sure that only actual headings are targeted. Sometimes, in markdown, there might be lines that start with \"##\" but aren't headings, like in code blocks or quotes. However, given the provided content, it seems all \"## \" lines are headings. So for this specific case, it's safe to proceed.\n",
      "\n",
      "Let me test this logic with the given content. The first line after # Document is \"## Introduction\". Change that to \"## 1. Introduction\". Next is \"## Background\" becomes \"## 2. Background\", and so on. Each subsequent heading increments the number. \n",
      "\n",
      "I'll process each line, keep track of the counter, and build the modified content. Once done, present the result to the user. Since the user didn't ask to save the changes, just to display the modified content, I don't need to use any write functions. Just output the processed content in the response.\n",
      "Here is the content of `example_document.md` with second-level headings numbered sequentially:\n",
      "\n",
      "# Document\n",
      "\n",
      "## 1. Introduction\n",
      "- Brief overview of the document's purpose and main topics.\n",
      "\n",
      "## 2. Background\n",
      "- Historical context and previous research.\n",
      "- Key theories and models discussed.\n",
      "\n",
      "## 3. Methodology\n",
      "- Detailed description of the methods used in the study.\n",
      "- Explanation of data collection and analysis techniques.\n",
      "\n",
      "## 4. Results\n",
      "- Presentation of the findings from the research.\n",
      "- Statistical analysis and graphical representations.\n",
      "\n",
      "## 5. Discussion\n",
      "- Interpretation of the results.\n",
      "- Comparison with previous studies.\n",
      "- Limitations and future research directions.\n",
      "\n",
      "## 6. Conclusion\n",
      "- Summary of the main points covered in the document.\n",
      "- Final thoughts and implications of the research."
     ]
    }
   ],
   "source": [
    "response_plain_text = ''\n",
    "for ret_messages in agent.run(messages):\n",
    "    # `ret_messages` will contain all subsequent messages, consisting of interleaved assistant messages and tool responses\n",
    "    response_plain_text = typewriter_print(ret_messages, response_plain_text)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "messages += ret_messages"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Step 3: Create a Mind Map\n",
    "\n",
    "We will create a mind map by prompting the agent, and it will call the corresponding tool."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "messages += [{'role': 'user', 'content': 'Create a mind map with your previous markdown output. Show me the saved path.'}]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[THINK]\n",
      "Okay, the user wants me to create a mind map from the previous markdown output and show the saved path. Let me recall the tools available. There's the mindmap-convert_markdown_to_mindmap function which takes markdown content and converts it to a mindmap. The previous response was modified markdown with numbered headings. \n",
      "\n",
      "First, I need to take that modified markdown content and pass it to the conversion function. The function returns either HTML content or a file path. Since the user asked for the saved path, I should check if the tool's response includes the file path.\n",
      "\n",
      "Wait, the tool's description says it returns either HTML content or the file path depending on the server argument. But as the assistant, I don't control the server arguments. However, the user is asking to \"show me the saved path,\" so I need to use the function and then present the path from the tool's response.\n",
      "\n",
      "So the steps are: use the mindmap-convert function with the modified markdown content, get the result, and then extract the file path from the tool's response to display it to the user. I need to make sure the function is called correctly with the right parameters. The markdown_content parameter is required, so I'll input the numbered markdown provided in the previous message.\n",
      "[TOOL_CALL] mindmap-convert_markdown_to_mindmap\n",
      "{\"markdown_content\": \"# Document\\n\\n## 1. Introduction\\n- Brief overview of the document's purpose and main topics.\\n\\n## 2. Background\\n- Historical context and previous research.\\n- Key theories and models discussed.\\n\\n## 3. Methodology\\n- Detailed description of the methods used in the study.\\n- Explanation of data collection and analysis techniques.\\n\\n## 4. Results\\n- Presentation of the findings from the research.\\n- Statistical analysis and graphical representations.\\n\\n## 5. Discussion\\n- Interpretation of the results.\\n- Comparison with previous studies.\\n- Limitations and future research directions.\\n\\n## 6. Conclusion\\n- Summary of the main points covered in the document.\\n- Final thoughts and implications of the research.\"}\n",
      "[TOOL_RESPONSE] mindmap-convert_markdown_to_mindmap\n",
      "/tmp/mindmap-5i04kfgo/input.html\n",
      "[THINK]\n",
      "Okay, the user asked to create a mind map from the previous markdown output and show the saved path. I used the mindmap-convert_markdown_to_mindmap function with the provided markdown content. The tool returned a file path: /tmp/mindmap-5i04kfgo/input.html.\n",
      "\n",
      "Now, I need to present this path to the user. The user likely wants to know where the file is saved so they can access it. I should confirm that the mind map was generated successfully and provide the path. Also, maybe mention that they can open this HTML file to view the mind map. Keep the response clear and straightforward.\n",
      "The mind map has been generated and saved to the following path:  \n",
      "`/tmp/mindmap-5i04kfgo/input.html`\n",
      "\n",
      "You can open this HTML file in a web browser to view the mind map visualization."
     ]
    }
   ],
   "source": [
    "response_plain_text = ''\n",
    "for ret_messages in agent.run(messages):\n",
    "    # `ret_messages` will contain all subsequent messages, consisting of interleaved assistant messages and tool responses\n",
    "    response_plain_text = typewriter_print(ret_messages, response_plain_text)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# We can preview the mind map in the browser!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Use the GUI\n",
    "\n",
    "We have explored the capabilities of the Qwen-Agent framework and Qwen models in processing files. We can also achieve this by using the GUI."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2025-05-01 21:25:44,068 - mcp_manager.py - 110 - INFO - Initializing MCP tools from mcp servers: ['filesystem', 'mindmap']\n",
      "2025-05-01 21:25:44,074 - mcp_manager.py - 245 - INFO - Initializing a MCP stdio_client, if this takes forever, please check the config of this mcp server: filesystem\n",
      "2025-05-01 21:25:45,110 - mcp_manager.py - 245 - INFO - Initializing a MCP stdio_client, if this takes forever, please check the config of this mcp server: mindmap\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "* Running on local URL:  http://127.0.0.1:7861\n",
      "\n",
      "To create a public link, set `share=True` in `launch()`.\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div><iframe src=\"http://127.0.0.1:7861/\" width=\"100%\" height=\"500\" allow=\"autoplay; camera; microphone; clipboard-read; clipboard-write;\" frameborder=\"0\" allowfullscreen></iframe></div>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from qwen_agent.gui import WebUI\n",
    "\n",
    "agent = Assistant(\n",
    "    name=\"Qwen File Processing Assistant\",\n",
    "    description=\"I can help with your file processing needs, ask me anything!\",\n",
    "    llm=llm_cfg,\n",
    "    function_list=tools\n",
    ")\n",
    "\n",
    "WebUI(agent).run()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
